# SPDX-License-Identifier: Apache-2.0

set(KCONFIG_BINARY_DIR ${SEMINIX_BINARY_DIR}/kconfig)
file(MAKE_DIRECTORY ${KCONFIG_BINARY_DIR})

# Folders needed for conf/mconf files (kconfig has no method of redirecting all output files).
# conf/mconf needs to be run from a different directory because of: GH-3408
file(MAKE_DIRECTORY ${SEMINIX_BINARY_DIR}/include/generated)

set(KCONFIG_ROOT ${SEMINIX_SOURCE_DIR}/Kconfig)

set(DOTCONFIG                  ${SEMINIX_BINARY_DIR}/.config)

if(DEFINED KCONFIG_FILE)
	file(REMOVE ${DOTCONFIG})
	unset(SEMINIX_DEFCONFIG)
	unset(SEMINIX_DEFCONFIG CACHE)
endif()

if(NOT SEMINIX_DEFCONFIG AND (NOT DEFINED KCONFIG_FILE))
  set(DEFCONFIG_TMP defconfig)
else()
  set(DEFCONFIG_TMP ${KCONFIG_FILE})
endif()

list(
  APPEND
  FIND_KCONFIG_DIR
  ${SEMINIX_SOURCE_DIR}/arch/${ARCH}/configs
)
foreach(dir ${FIND_KCONFIG_DIR})
  find_file(SEMINIX_DEFCONFIG ${DEFCONFIG_TMP} HINTS ${dir})
  if (SEMINIX_DEFCONFIG STREQUAL SEMINIX_DEFCONFIG-FOUND)
    break()
  endif()
endforeach()

if (SEMINIX_DEFCONFIG STREQUAL SEMINIX_DEFCONFIG-NOTFOUND)
  message(FATAL_ERROR "Not Found KCONFIG_FILE. The example:
  -DKCONFIG_FILE=defconfig
  ")
endif()

unset(KCONFIG_FILE)
unset(KCONFIG_FILE CACHE)

message(STATUS "Kconfig file: ${SEMINIX_DEFCONFIG}")

set(COMMON_KCONFIG_ENV_SETTINGS
	srctree=${SEMINIX_SOURCE_DIR}
	KCONFIG_CONFIG=${DOTCONFIG}
  SEMINIX_VERSION_PREFIX=${SEMINIX_VERSION_PREFIX}
	ARCH=${ARCH}
	CC=${CMAKE_C_COMPILER}
	LD=${CMAKE_LINKER}
  CLANG_FLAGS=${CLANG_FLAGS}
)

foreach(kconfig_target
      alldefconfig
      allnoconfig
      allyesconfig
      menuconfig
      oldconfig
      olddefconfig
      )
  add_custom_target(
    ${kconfig_target}
    COMMAND ${CMAKE_COMMAND} -E env
    ${COMMON_KCONFIG_ENV_SETTINGS}
    ${kconfig_target} ${KCONFIG_ROOT}
    COMMAND ${CMAKE_COMMAND} -E env
    ${COMMON_KCONFIG_ENV_SETTINGS}
    genconfig
    --config-out ${KCONFIG_BINARY_DIR}/config-out
    --sync-deps ${KCONFIG_BINARY_DIR}/deps
    --file-list ${KCONFIG_BINARY_DIR}/file-list
    --env-list ${KCONFIG_BINARY_DIR}/env-list
    --header-path ${AUTOCONF_H}
    WORKING_DIRECTORY ${KCONFIG_BINARY_DIR}
    USES_TERMINAL
    COMMAND_EXPAND_LISTS
  )
endforeach()

add_custom_target(
  savedefconfig
  COMMAND ${CMAKE_COMMAND} -E env
  ${COMMON_KCONFIG_ENV_SETTINGS}
  savedefconfig
  --kconfig ${KCONFIG_ROOT}
  --out ${KCONFIG_BINARY_DIR}/defconfig
  WORKING_DIRECTORY ${KCONFIG_BINARY_DIR}
  USES_TERMINAL
  COMMAND_EXPAND_LISTS
)

# Support assigning Kconfig symbols on the command-line with CMake
# cache variables prefixed with 'CONFIG_'. This feature is
# experimental and undocumented until it has undergone more
# user-testing.
unset(EXTRA_KCONFIG_OPTIONS)
get_cmake_property(cache_variable_names CACHE_VARIABLES)
foreach (name ${cache_variable_names})
  if("${name}" MATCHES "^CONFIG_")
    # When a cache variable starts with 'CONFIG_', it is assumed to be
    # a Kconfig symbol assignment from the CMake command line.
    set(EXTRA_KCONFIG_OPTIONS
      "${EXTRA_KCONFIG_OPTIONS}\n${name}=${${name}}"
      )
  endif()
endforeach()

if(EXTRA_KCONFIG_OPTIONS)
  set(EXTRA_KCONFIG_OPTIONS_FILE ${SEMINIX_BINARY_DIR}/kconfig/generated/extra_kconfig_options.conf)
  file(WRITE
    ${EXTRA_KCONFIG_OPTIONS_FILE}
    ${EXTRA_KCONFIG_OPTIONS}
    )
endif()

set(
  merge_config_files
  ${EXTRA_KCONFIG_OPTIONS_FILE}
)

# Create a list of absolute paths to the .config sources from
# merge_config_files, which is a mix of absolute and relative paths.
set(merge_config_files_with_absolute_paths "")
foreach(f ${merge_config_files})
  if(IS_ABSOLUTE ${f})
    set(path ${f})
  else()
    set(path ${SEMINIX_SOURCE_DIR}/${f})
  endif()

  list(APPEND merge_config_files_with_absolute_paths ${path})
endforeach()

foreach(f ${merge_config_files_with_absolute_paths})
  if(NOT EXISTS ${f} OR IS_DIRECTORY ${f})
    message(FATAL_ERROR "File not found: ${f}")
  endif()
endforeach()

# Calculate a checksum of merge_config_files to determine if we need
# to re-generate .config
set(merge_config_files_checksum "")
foreach(f ${merge_config_files_with_absolute_paths})
  file(MD5 ${f} checksum)
  set(merge_config_files_checksum "${merge_config_files_checksum}${checksum}")
endforeach()

# Create a new .config if it does not exists, or if the checksum of
# the dependencies has changed
set(merge_config_files_checksum_file ${SEMINIX_BINARY_DIR}/.cmake.dotconfig.checksum)
set(CREATE_NEW_DOTCONFIG 1)
# Check if the checksum file exists too before trying to open it, though it
# should under normal circumstances
if(EXISTS ${DOTCONFIG} AND EXISTS ${merge_config_files_checksum_file})
  # Read out what the checksum was previously
  file(READ
    ${merge_config_files_checksum_file}
    merge_config_files_checksum_prev
    )
  if(
    ${merge_config_files_checksum} STREQUAL
    ${merge_config_files_checksum_prev})
    # Checksum is the same as before
    set(CREATE_NEW_DOTCONFIG 0)
  endif()
endif()

if(NOT EXISTS ${DOTCONFIG})
  execute_process(
    COMMAND ${CMAKE_COMMAND} -E env
    ${COMMON_KCONFIG_ENV_SETTINGS}
    defconfig
    --kconfig ${KCONFIG_ROOT} ${SEMINIX_DEFCONFIG}
    COMMAND ${CMAKE_COMMAND} -E env
    ${COMMON_KCONFIG_ENV_SETTINGS}
    genconfig
    --config-out ${KCONFIG_BINARY_DIR}/config-out
    --sync-deps ${KCONFIG_BINARY_DIR}/deps
    --file-list ${KCONFIG_BINARY_DIR}/file-list
    --env-list ${KCONFIG_BINARY_DIR}/env-list
    --header-path ${AUTOCONF_H}
    WORKING_DIRECTORY ${KCONFIG_BINARY_DIR}
    RESULT_VARIABLE ret
    )
  if(NOT "${ret}" STREQUAL "0")
    message(FATAL_ERROR "command failed with return code: ${ret}")
  endif()
endif()

if(CREATE_NEW_DOTCONFIG)
  # Write the new configuration fragment checksum. Only do this if kconfig.py
  # succeeds, to avoid marking seminix/.config as up-to-date when it hasn't been
  # regenerated.
  file(WRITE ${merge_config_files_checksum_file}
                        ${merge_config_files_checksum})
endif()

# Read out the list of 'Kconfig' sources that were used by the engine.
file(STRINGS ${KCONFIG_BINARY_DIR}/file-list PARSED_KCONFIG_SOURCES_LIST)

# Force CMAKE configure when the Kconfig sources or configuration files changes.
foreach(kconfig_input
      ${merge_config_files}
      ${DOTCONFIG}
      ${PARSED_KCONFIG_SOURCES_LIST}
      )
  set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig_input})
endforeach()

add_custom_target(config-twister DEPENDS ${DOTCONFIG})

# Remove the CLI Kconfig symbols from the namespace and
# CMakeCache.txt. If the symbols end up in DOTCONFIG they will be
# re-introduced to the namespace through 'import_kconfig'.
foreach (name ${cache_variable_names})
  if("${name}" MATCHES "^CONFIG_")
    unset(${name})
    unset(${name} CACHE)
  endif()
endforeach()

# Parse the lines prefixed with CONFIG_ in the .config file from Kconfig
import_kconfig(CONFIG_ ${DOTCONFIG})

# Re-introduce the CLI Kconfig symbols that survived
foreach (name ${cache_variable_names})
  if("${name}" MATCHES "^CONFIG_")
    if(DEFINED ${name})
      set(${name} ${${name}} CACHE STRING "")
    endif()
  endif()
endforeach()

set(KCONFIG_H ${SEMINIX_BINARY_DIR}/include/generated/kconfig.h CACHE PATH "")

execute_process(
  COMMAND ${PYTHON_EXECUTABLE}
  ${SEMINIX_SOURCE_DIR}/scripts/mkkconfig_h.py
  -o ${KCONFIG_H}
  WORKING_DIRECTORY ${SEMINIX_BINARY_DIR}
  RESULT_VARIABLE ret
)
if(NOT "${ret}" STREQUAL "0")
  message(FATAL_ERROR "command failed with return code: ${ret}")
endif()
